﻿using System;
using ParboiledSharp.parboiled;
using ParboiledSharp.parboiled.dataobjects;
using ParboiledSharp.parboiled.rules;

namespace ParboiledSharp
{
    class TestRule : BaseParser
    {
        #region ToBeHidden
        private Rule _expression;
        public Rule Expression()
        {
            return _expression ?? Expression(ref _expression);
        }
        private Rule _term;
        public Rule Term()
        {
            return _term ?? Term(ref _term);
        }
        private Rule _factor;
        public Rule Factor()
        {
            return _factor ?? Factor(ref _factor);
        }
        private Rule _parens;
        public Rule Parens()
        {
            return _parens ?? Parens(ref _parens);
        }
        private Rule _number;
        public Rule Number()
        {
            return _number ?? Number(ref _number);
        }
        private Rule _digits;
        public Rule Digits()
        {
            return _digits ?? Digits(ref _digits);
        } 
        #endregion

        public Rule Expression(ref Rule rule)
        {
            rule= SEQUENCE();
            return rule.SetRules(Term(),
                    ZEROORMORE(
                            FIRST(
                                    SEQUENCE(
                                        '+', 
                                        Term(), 
                                        ACTION(c => { c.Push((int)c.Pop() + (int)c.Pop()); })
                                    ),
                                    SEQUENCE(
                                        '-', 
                                        Term(), 
                                        ACTION(c => {c.Push((int)c.Pop(1) - (int)c.Pop()); })
                                    )
                            )
                    ));
        }

        public Rule Term(ref Rule rule)
        {
            rule= SEQUENCE();
            return rule.SetRules(
                    Factor(),
                    ZEROORMORE(
                            FIRST(
                                    SEQUENCE('*', Factor(),
                                    ACTION(c=> { c.Push((int)c.Pop() * (int)c.Pop()); })),
                                    SEQUENCE('/', Factor(), ACTION(c=>{c.Push((int)c.Pop(1) / (int)c.Pop());}))
                            )
                    ));
        }

        public System.Func<bool> Term1 = delegate()
        {
            return true;
        };
        
        public Rule Factor(ref Rule rule)
        {
            rule= FIRST(); // a factor "produces" exactly one Integer value on the value stack
            return rule.SetRules(Number(), Parens());
        }

        public Rule Parens(ref Rule rule)
        {
            rule= SEQUENCE();
            return rule.SetRules('(', Expression(), ')');
        }

        public Rule Number(ref Rule rule)
        {
            rule=SEQUENCE();
            return rule.SetRules(
                    Digits(),
                    ACTION(c=>{c.Push(int.Parse(c.GetMatch()));}));
        }

        public Rule Digits(ref Rule rule)
        {
            rule= ONEORMORE();
            return rule.SetRule(Digit());
        }

        public Rule Digit()
        {
            return CHARRANGE('0', '9');
        }
    }
}
